<template>
  <!-- 这里label-width设置标签的长度。作为 Form 直接子元素的 form-item 会继承该值。-->
  <el-form label-width="100px">
    <el-form-item label="SPU名称">
      <el-input placeholder="请你输入SPU名称" v-model="SpuParams.spuName"></el-input>
    </el-form-item>
    <el-form-item label="SPU品牌">
      <el-select v-model="SpuParams.tmId" value-key="" placeholder="请你选择品牌" @change="">
        <!-- value="item.id" 表示el-select收集的值是品牌的id，所以v-model=SpuParams.tmId 会正常显示出选中的品牌名称 -->
        <el-option v-for="(item) in allTrademark" :key="item.id" :label="item.tmName" :value="item.id"></el-option>
      </el-select>
    </el-form-item>
    <el-form-item label="SPU描述">
      <el-input type="textarea" placeholder="请你输入SPU描述" v-model="SpuParams.description"></el-input>
    </el-form-item>
    <el-form-item label="SPU照片">
      <!-- 展示照片墙 -->
      <!-- 
        v-model file-list->展示的默认图片
        action: 上传图片的接口地址,前面说过了这里必须在接口前面加上/api
        list-card: 文件列表的类型 'text' | 'picture' | 'picture-card'
        on-preview: 文件预览的钩子
       -->
      <el-upload
        v-model:file-list="imgList"
        action="/api/admin/product/fileUpload"
        list-type="picture-card"
        :on-preview="handlePictureCardPreview"
        :on-remove="handleRemove"
        :before-upload="beforeSpuPictureUpload"
      >
        <el-icon><Plus /></el-icon>
      </el-upload>

      <el-dialog v-model="dialogVisible">
        <img w-full :src="dialogImageUrl" alt="Preview Image" style="width: 100%; height: 100%"/>
      </el-dialog>
    </el-form-item>

    <el-form-item label="SPU销售属性">
      <!-- 展示销售属性的下拉菜单 -->
      <el-select value-key="" :placeholder="unSelectSaleAttr.length ? `还未选择${unSelectSaleAttr.length}个`: '无'" clearable filterable v-model="selectSaleAttrId">
        <el-option v-for="item in unSelectSaleAttr" :key="item.id" :label="item.name" :value="item.id"></el-option>
      </el-select>
      <el-button style="margin-left: 10px" type="primary" size="default" @click="addSaleAttr" icon="Plus" :disabled="selectSaleAttrId ? false : true">
        添加销售属性
      </el-button>
      <!-- table展示销售属性与属性值 -->
      <el-table border style="margin: 10px 0px" :data="saleAttr">
        <el-table-column label="序号" align="center" width="80px" type="index"></el-table-column>
        <el-table-column label="属性名" width="120px" prop="saleAttrName"></el-table-column>
        <el-table-column label="属性值">
          <!-- row:即为当前SPU已有的销售属性对象 -->
          <template #="{ row }">
            <el-tag style="margin: 0px 5px" v-for="(item, index) in row.spuSaleAttrValueList" :key="index" closable @close="row.spuSaleAttrValueList.splice(index, 1)">
              {{ item.saleAttrValueName }}
            </el-tag>
            <el-input
              v-if="row.flag == true"
              placeholder="请输入属性值"
              size="small"
              style="width: 100px; margin: 0px 5px"
              :ref="(vc: any)=> inputAttrValue = vc"
              v-model="row.saleAttrValue"
              @blur="toViewAttrValue(row)"
            ></el-input>
            <el-button v-else style="0px 5px" type="success" size="small" icon="Plus" @click="toEditAttrValue(row)"></el-button>
          </template>
        </el-table-column>
        <el-table-column label="操作" width="120px">
          <template #="{ $index }">
            <el-button type="danger" size="small" icon="Delete" @click="saleAttr.splice($index, 1)">
              删除
            </el-button>
          </template>
        </el-table-column>
      </el-table>
    </el-form-item>
    <el-form-item>
      <el-button type="primary" size="default" @click="save">保存</el-button>
      <el-button size="default" @click="cancel">取消</el-button>
    </el-form-item>
  </el-form>
</template>
<script setup lang="ts">
import { ref, computed, nextTick } from 'vue'
// 引入ts类型
import type {
  SpuData,
  Trademark,
  SpuImg,
  SaleAttr,
  AllTrademark,
  SpuHasImg,
  HasSaleAttr,
  SaleAttrResponseData,
  HasSaleAttrResponseData,
} from '@/api/product/spu/type'
// 引入api
import {
  reqAllTradeMark,
  reqImageList,
  reqSaleAttrList,
  reqALlSaleAttr,
  reqAddOrUpdateSPU,
} from '@/api/product/spu'
import { ElMessage } from 'element-plus/lib/components/index.js'
import { SaleAttrValue } from '@/api/product/spu/type'
//利用defineEmits方法返回的函数去触发自定义事件
let $emit = defineEmits(['changeScene'])

// 取消添加SPU
const cancel = () => {
  // 通过父组件切换场景0, action为add表示新增，添加成功后跳到第一页；为update表示修改，跳到默认即当前页;
  $emit('changeScene', {scenes: 0, action: ''})
}
// 存储已有的SPU的数据
let allTrademark = ref<Trademark[]>([])
// 商品图片
let imgList = ref<SpuImg[]>([])
// 已有的SPU销售属性
let saleAttr = ref<SaleAttr[]>([])
// 全部销售属性
let allSaleAttr = ref<HasSaleAttr[]>([])
// 将来收集还未选择的销售属性的ID
let selectSaleAttrId = ref<number | string>('')

// 存储已有的SPU对象
let SpuParams = ref<SpuData>({
  category3Id: '', // 收集的三级分类的ID
  spuName: '', // SPU名字
  description: '', // SPU描述
  tmId: '', // 品牌的ID
  spuImageList: [],
  spuSaleAttrList: [],
})

// 控制图片对话框的显示与隐藏效果
let dialogVisible = ref<boolean>(false)
// 存储预览图片的地址
let dialogImageUrl = ref<string>('')

// 准备一个变量来存储，添加修改结构中需要输入属性名称input实例对象，从而在用户点击"添加属性"用来获取input焦点
let inputAttrValue = ref<any>()

// 子组件方法: 用来获取编辑时需要展示的全部数据
const initSpuForm = async (spu: SpuData) => {
  // 存储已有的SPU对象，将来在模板中展示
  SpuParams.value = spu

  // 父组件传递过来的已有的SPU对象，不完整，需要另外发送4个api请求获取完整的SPU对象的数据
  // 获取全部的SPU的品牌的数据
  let result1: AllTrademark = await reqAllTradeMark()
  // 获取某一个已有的SPU下的全部商品的照片地址
  let result2: SpuHasImg = await reqImageList(spu.id as number)
  // 获取某一个已有的SPU下有多少个销售属性
  let result3: SaleAttrResponseData = await reqSaleAttrList(spu.id as number)
  // 获取全部的销售属性
  let result4: HasSaleAttrResponseData = await reqALlSaleAttr()

  // 存储全部的品牌数据
  allTrademark.value = result1.data
  // SPU对应的商品图片（由于el-upload的照片墙组件，要想正常展示出图片，需要name和url的属性，分别代表图片名和图片url，所以这里我们需要改造）
  imgList.value = result2.data.map((item) => {
    return {
      name: item.imgName,
      url: item.imgUrl,
    }
  })
  // 存储已有的SPU的销售属性
  saleAttr.value = result3.data
  // 存储全部的商品属性
  allSaleAttr.value = result4.data
}

// 照片墙点击预览的时候触发
const handlePictureCardPreview = (uploadFile: any) => {
  // 把上传的图片的url赋值给对话框中的要展示的图片地址的变量
  dialogImageUrl.value = uploadFile.url
  // 弹出对话框
  dialogVisible.value = true
}

// 照片墙删除图片的钩子（组件内部实现了当点击删除的图标时，自动从fileList中移除了对于图片的url）
const handleRemove = () => {
  console.log(123)
}

// 照片上传成功之前的钩子--约束文件的大小与类型
const beforeSpuPictureUpload = (file: any) => {
  if (
    file.type == 'image/png' ||
    file.type == 'image/jpg' ||
    file.type == 'image/jpeg' ||
    file.type == 'image/svg+xml'
  ) {
    if (file.size / 1024 / 1024 < 3) {
      return true
    } else {
      ElMessage({
        type: 'error',
        message: '上文的文件务必小于3M',
      })
      return false
    }
  } else {
    ElMessage({
      type: 'error',
      message: '上文的文件务必是PNG|JPG|GIF|svg',
    })
    return false
  }
}
// 计算出当前SPU还未拥有的销售属性
let unSelectSaleAttr = computed(() => {
  // 在全部的销售属性中过滤出saleAttr中的saleAttrName不等于全部销售属性中name的元素
  // some() 方法是 JavaScript 数组的一个内置方法，它用于检查数组中是否至少有一个元素满足指定的条件
  let unSelectArr = allSaleAttr.value.filter(
    (item1) =>
      !saleAttr.value.some((item2) => item1.name === item2.saleAttrName),
  )
  return unSelectArr
})

// 添加销售属性
const addSaleAttr = () => {
  // 把新的属性添加到已有的属性中
  let newSaleAttr: SaleAttr = {
    baseSaleAttrId: selectSaleAttrId.value,
    saleAttrName: allSaleAttr.value.filter(
      (item) => item.id == selectSaleAttrId.value,
    )[0].name, // 通过属性id或者属性名字
    spuSaleAttrValueList: [],
  }
  saleAttr.value.push(newSaleAttr)
  // 添加完需要把selectSaleAttrId的值清空
  selectSaleAttrId.value = ''
}

// 属性值按钮的点击事件
const toEditAttrValue = (row: SaleAttr) => {
  console.log('row->', row)
  // 切换为编辑模式，即el-input展示出来
  row.flag = true
  // 获取input焦点(由于响应式数据刚刚发生变化，我们无法获取dom，所以使用nextTick)
  nextTick(() => {
    inputAttrValue.value.focus()
  })
  // 设置一个临时变量收集用户输入框输入的值
  row.saleAttrValue = ''
}

// 表单元素失去焦点的回调
const toViewAttrValue = (row: SaleAttr) => {
  // 把添加的属性值放入属性值的列表中
  const { baseSaleAttrId, saleAttrValue } = row
  // 整理成服务器所需要的属性值形式
  let newSaleAttrValue: SaleAttrValue = {
    baseSaleAttrId,
    saleAttrValueName: saleAttrValue as string,
  }

  // 对于属性值是否为空
  if (saleAttrValue?.trim() == '') {
    ElMessage({
      type: 'error',
      message: '属性值不能为空',
    })
    return
  }
  // 判断属性值是否在数组当中存在
  let repeat = row.spuSaleAttrValueList.find((item) => {
    return item.saleAttrValueName == saleAttrValue
  })
  if (repeat) {
    ElMessage({
      type: 'error',
      message: '属性值不能重复',
    })
    return
  }

  row.spuSaleAttrValueList.push(newSaleAttrValue)
  // input框消失，添加属性的按钮展示出来，即有编辑模式切换为查看模式
  row.flag = false
}

// 保存按钮的回调（添加SPU | 更新已有的SPU）
const save = async () => {
  // 整理参数
  // 1. 照片墙的数据
  // 由于我们前面为了使用el-upload组件的照片墙功能，更改了系统默认的图片的名称和url地址的字段名，这里我们需要改回来
  // 把name和url，改回imgName和imgUrl

  SpuParams.value.spuImageList = imgList.value.map((item: any) => {
    return {
      imgName: item.name,
      imgUrl: (item.response && item.response.data) || item.url,
      // 对于新增的图片,服务器会返回数据。要用 item.response && item.response.data 来获取图片地址
    }
  })
  // 2. 整理好销售属性的数据
  SpuParams.value.spuSaleAttrList = saleAttr.value
  console.log('整理好的数据->', SpuParams.value)

  // 发送请求
  let result = await reqAddOrUpdateSPU(SpuParams.value)
  if(result.code == 200){
    ElMessage({
      type: 'success',
      message: SpuParams.value.id?"修改成功":"添加成功"
    })
    // 添加成功后，通知父组件切换场景以及具体的操作是不是新增，如果是新增需要跳转到第一页，否则默认当前页,并且让父组件切换成功后再次获取已有的SPU数据
    $emit('changeScene', {scenes: 0, action: SpuParams.value.id?'update':'add'})
    
  } else {
    ElMessage({
      type: 'error',
      message: SpuParams.value.id?`修改失败:${result.data}`:`添加失败:${result.data}`
    })
  }
}

// 添加一个新的SPU初始化请求的方法
// 对外暴露
const initAddSpu = async (c3id: number | string)=> {
  // 由于是新增SPU,所以这里不需要获取照片墙以及一个已有的SPU下有多少个销售属性，只需要获取 全部的SPU的品牌的数据和全部的销售属性
  
  // 1. 清空原来的数据
  Object.assign(SpuParams.value, {
    id: '', // 这里的id也需要加上，否则会出现先修改时id有值，导致下次新增时系统也会默认为修改，所以必须要清空
    category3Id: '', 
    spuName: '', // SPU名字
    description: '', // SPU描述
    tmId: '', // 品牌的ID
    spuImageList: [],
    spuSaleAttrList: [],
  })
  // 清空销售属性和照片墙
  saleAttr.value = []
  imgList.value = []
  // 清空用户选择的销售属性的ID(用户本来要新增，最新填完数据直接取消了，为了避免下次再添加时出现之前选中的值)
  selectSaleAttrId.value = ''

  // 2. 收集必须的数据
  SpuParams.value.category3Id = c3id // 这里为了省事直接从父组件传递的参数获取了，也可以直接从分类仓库中直接获取
  // 获取全部的SPU的品牌的数据
  let result1: AllTrademark = await reqAllTradeMark()
  // 获取全部的销售属性
  let result4: HasSaleAttrResponseData = await reqALlSaleAttr()
  // 存储全部的品牌数据
  allTrademark.value = result1.data
  // 存储全部的商品属性
  allSaleAttr.value = result4.data
}

defineExpose({
  initSpuForm,
  initAddSpu
})
</script>

<script lang="ts">
export default {
  name: 'SpuForm',
}
</script>
<style scoped></style>
